package io.gitee.zhangbinhub.acp.cloud.aspect

import com.fasterxml.jackson.core.JsonProcessingException
import io.gitee.zhangbinhub.acp.boot.exceptions.WebException
import io.gitee.zhangbinhub.acp.cloud.annotation.AcpCloudDuplicateSubmission
import io.gitee.zhangbinhub.acp.cloud.lock.DistributedLock
import io.gitee.zhangbinhub.acp.core.common.security.Md5Encrypt
import org.noear.snack4.ONode
import org.noear.solon.core.aspect.Invocation
import org.noear.solon.core.aspect.MethodInterceptor
import org.noear.solon.core.handle.Context

/**
 * controller拦截器
 *
 * @since JDK 17
 */
class RestControllerRepeatAspect(private val distributedLock: DistributedLock) : MethodInterceptor {
    @Throws(Throwable::class)
    override fun doIntercept(inv: Invocation): Any =
        distributedLock.let { lock ->
            val signature = inv.method()
            val method = signature.method
            val duplicateSubmission = method.getAnnotation(AcpCloudDuplicateSubmission::class.java)!!
            val key = getKey(
                signature.declaringClz.canonicalName + "." + method.name,
                duplicateSubmission.keyExpress,
                inv.args()
            )
            val expire = duplicateSubmission.expire
            if (lock.getLock(key, expire, false)) {
                try {
                    inv.invoke()
                } finally {
                    lock.releaseLock(key)
                }
            } else {
                throw WebException("请勿重复请求")
            }
        } ?: inv.invoke()

    private fun getKey(prefix: String, keyExpress: String, args: Array<Any>): String {
        val builder = StringBuilder()
        for (arg in args) {
            if (arg !is Context) {
                builder.append(",")
                if (arg is Int || arg is Long
                    || arg is Float || arg is Double || arg is Boolean
                    || arg is String || arg is Char || arg is Byte
                ) {
                    builder.append(arg.toString())
                } else {
                    try {
                        builder.append(ONode.serialize(arg))
                    } catch (e: JsonProcessingException) {
                        builder.append(arg.toString())
                    }
                }
            }
        }
        val keyValue = prefix + ":" + Md5Encrypt.encrypt(builder.toString())
        return keyExpress.replace(AcpCloudDuplicateSubmission.DEFAULT_KEY_EXPRESS, keyValue)
    }

}
